1. Field of the Invention
The invention relates generally to programming microprocessors and more particularly to a method for determining the default size of operations in a code segment using instructions which are available to multiple privilege levels in multiple operating modes.
2. Description of the Relevant Art
One of the functions of an operating system in a computer is to provide a simplified environment in which a software application can be executed. In the x86 architecture, the memory management function of the operating system creates separate logical address spaces for each of the applications executing on the system. By doing so, the memory management mechanism eliminates the need for each of the applications to individually check its memory references in order to avoid using memory locations used by other applications. The applications are thus allowed to execute without interference from each other.
The x86 architecture implements separation of the address spaces for the respective programs using a memory model referred to as segmentation. Segmentation provides each of the applications with an address space having several separate, protected segments. The segments appear to the application to be contiguous so that there is no need for the programmer to write position-dependent code. Memory references to the individual segments may only refer to data in those segments so that, for example, a stack cannot grow into the code or data of an application. The limits of the segments are enforced by hardware in the computer system. (Particular segments can, however, be shared by more than one application.) Memory addresses which appear to an application to be fixed are referenced by the operating system as offsets from the beginning of one of the segments.
Referring to FIG. 1, a block diagram of a typical memory management model for a system having an x86 architecture is shown. The fixed, logical address used by an application is translated by the segmentation mechanism into a linear address. If the computer system does not have a paging mechanism, the linear address is also the physical address of the memory location. The paging mechanism swaps "pages" of memory between the memory and the system's hard disk or other persistent storage devices to simulate an address space which is larger than the available memory. If a paging mechanism is implemented, this mechanism translates the linear address into a physical address.
The operating system's memory management mechanism keeps track of the various segments by using segment descriptors. Segment descriptors contain data defining the size and location of the segments, as well as control and status information. The segment descriptors are stored in an array called the segment descriptor table. There are actually two types of segment descriptor table: the global descriptor table (GDT); and local descriptor tables (LDT). The GDT contains segment descriptors for all tasks (applications being executed.) There is an LDT for each task which includes descriptors for only that task. A descriptor table can have a variable length and, in the x86 architecture, may contain up to 8192 (2.sup.13) segment descriptors. The first entry in the GDT is a null descriptor which may be used to initialize segment registers. A memory reference to the null descriptor will generate an exception.
Referring to FIG. 2, a diagram illustrating the components of a segment descriptor is shown. Each segment descriptor contains information on one of the segments, including the size and location of the segment. The size and location are also referred to as the segment limit and base address, respectively. In the x86 architecture, the segment limit uses 20 bits of the descriptor. The descriptor also includes a granularity bit which, if set, scales the limit field by a factor of 4096 (2.sup.12). The base address uses 32 bits of the descriptor. The remaining bits of the descriptor comprise an S-bit (which identifies the segment as a code/data segment or a system segment), a D-bit/B-bit (which indicates the default length for operands and addresses in code/data segments), a type field, a descriptor-privilege-level bit and a segment-present bit.
A particular segment can be accessed by loading the appropriate segment selector into one of the six segment registers available in the x86 architecture. These registers store segment selectors for a code segment (CS), a stack segment (SS) and up to four data segments (DS, ES, FS, GS). While an application can have more than six segments, only six can occupy the segment registers, so only those six are ready for immediate use at any given time. The segment selector includes two bits which indicate the privilege level associated with the segment, a bit which indicates whether the segment descriptor is in the GDT or the LDT, and a 13-bit index into the respective descriptor table.
Occasionally, it may be necessary or useful for a programmer to determine the default execution mode of the current code segment. That is, the programmer may need to determine whether the default size of operands and instructions in the code segment is 16 bits or 32 bits. A programmer who is writing library routines for use by both 16-bit and 32-bit programs, for example, may need to incorporate code to determine the default operating mode. Determining the default operating mode is accomplished by accessing the D-bit/B-bit in the segment descriptor. As pointed out above, the D-bit/B-bit in a segment descriptor indicates the default length for operands and addresses in corresponding code/data segments In the prior art, the D-bit/B-bit could be accessed using the following code sequence:
(1) XOR EDX, EDX (2) MOV DX, CS (3) AND DX, 0f8h (4) SGDT gdt_loc (5) MOV EAX, gdt_loc (6) MOV EBX, EAX+2 (7) ADD EBX, EDX (8) MOV EAX, [EDX+4] (9) TEST EAX, 04000000h (10) JNZ @32BIT
This code sequence is illustrative of the numerous steps which must be taken simply to access the code segment's D-bit/B-bit. The code sequence represents the steps of:
(1) XORing the 32-bit EDX register with itself to zero each bit in the register; PA1 (2) moving the 16-bit segment selector for the code segment into the DX register (which forms the lower 16 bits of EDX); PA1 (3) zeroing the lower 3 bits of DX by ANDing the segment selector value with f8 (because the last three bits are used to indicate the privilege level and descriptor table, while the upper 13 bits form an index into the table); PA1 (4) placing the address of the GDT in the variable gdt_loc (the SGDT instruction is protected and can only be executed by the operating system); PA1 (5) placing the address of the GDT in register EAX (this must be done separately from the previous step because the GDT address can only be retrieved through the SGDT instruction); PA1 (6) placing in register EBX the location of the base address of the first entry in the GDT; PA1 (7) adding to EBX the index portion (scaled by 8) of the code segment selector (i.e., placing in EBX the location of the base address of the code segment entry in the GDT); PA1 (8) moving into register EAX the upper 32 bits of the code segment descriptor; PA1 (9) testing the 22.sup.nd bit of the value in EAX (the D-bit/B-bit) to determine whether or not it is set, PA1 (10) if the D-bit/B-bit is set, jumping to the label @32BIT.
This method of determining the default operating mode is direct in that it determines the mode from the bit which defines the mode. The method has several drawbacks, however. For example, the user application must be operating in Protected Mode (in which the full 32-bit architecture of the computer system is available to the user.) The method is also relatively time-consuming in that it requires ten instructions simply to determine the state of a single bit in the descriptor.